#include "Material.hpp"
#include <iostream>

// declaration of static members
map<string,LME_VertexShader*> MaterialGestionner::m_vertexShaderMap;
map<string,LME_FragmentShader*> MaterialGestionner::m_fragmentShaderMap;
map<string,LME_ShaderProgram*> MaterialGestionner::m_programShaderMap;
map<string,Material*> MaterialGestionner::m_materialMap;
map<string,GLuint> MaterialGestionner::m_textureMap;

map<string,GenericImage*> MaterialGestionner::m_imageMap;

Material* MaterialGestionner::addTexturedMaterial(string name, string texture, string vertexShader, string fragShader){
  // texture loading
  GLuint textureId = getTexture(texture);//loadTGATexture(texture.c_str());
  // shader loading
  LME_ShaderProgram* shaderProgram = getShaderProgram(name,vertexShader,fragShader);
  //new ShaderProgram((name+"Shader").c_str(),vertexShader,fragShader);
  // TexturedMaterial creation
  ShaderTexturedMaterial* material = new ShaderTexturedMaterial();
  material->setShader(shaderProgram);
  material->setTexture(textureId);
  return material;
}


Material* MaterialGestionner::addMaterial(string name, string vertexShader, string fragShader) {
  LME_ShaderProgram* shaderProgram = getShaderProgram(name,vertexShader,fragShader);
  ShaderMaterial* material = new ShaderMaterial();
  material->setShader(shaderProgram);
  return material;
}

LME_VertexShader* MaterialGestionner::getVertexShader(string name) {
	// on regarde si le programme du shader existe déj)
	map<string,LME_VertexShader*>::iterator it= m_vertexShaderMap.find(name);
	if (it != m_vertexShaderMap.end()) return (*it).second;
	// sinon on le crée
	std::cout << "Shader creation ... " << name << std::endl;
	LME_VertexShader* m_vertexShader = new LME_VertexShader(name.c_str());
	m_vertexShaderMap[name] = m_vertexShader;
	std::cout << " DONE " << endl;
	return m_vertexShader;
}

LME_FragmentShader* MaterialGestionner::getFragmentShader(string name) {
	// on regarde si le programme du shader existe déj)
	map<string,LME_FragmentShader*>::iterator it= m_fragmentShaderMap.find(name);
	if (it != m_fragmentShaderMap.end()) return (*it).second;
	// sinon on le crée
	std::cout << "Fragment Shader Creation ... " << name;
	LME_FragmentShader* m_fragmentShader = new LME_FragmentShader(name.c_str());
	m_fragmentShaderMap[name] = m_fragmentShader;
	std::cout << " DONE " << std::endl;
	return m_fragmentShader;
}

LME_ShaderProgram* MaterialGestionner::getShaderProgram(string name, string vertexShaderName, string fragmentShaderName) {
	// on regarde si le programme du shader existe déj)
	map<string,LME_ShaderProgram*>::iterator it= m_programShaderMap.find(name);
	if (it != m_programShaderMap.end()) return (*it).second;
	// sinon on le crée
	LME_VertexShader* m_vertexShader = getVertexShader(vertexShaderName);
	LME_FragmentShader* m_fragmentShader = getFragmentShader(fragmentShaderName);
	LME_ShaderProgram* m_programShader = new LME_ShaderProgram(name.c_str(),*m_vertexShader,*m_fragmentShader);
	m_programShaderMap[name] = m_programShader;
	return m_programShader;
}

GLuint MaterialGestionner::getTexture(string name) {
	// texture vide
	if (name == "none") return 0;
	map<string,GLuint>::iterator it = m_textureMap.find(name);
	if (it != m_textureMap.end()) return (*it).second;
	// si la texture n'est pas défini, on la crée
	GenericImage* img = getImage(name);
	//GLuint text = loadTGATexture(name.c_str());
	cout << "loading texture : " << name << " ... ";
	GLuint text = img->defineTextureFromImage();
	cout << " being bind to id " << text << " DONE ." << endl;
	m_textureMap[name] = text;
	return text;
}

GenericImage* MaterialGestionner::getImage(string filename) {
	map<string, GenericImage*>::iterator it = m_imageMap.find(filename);
	if (it != m_imageMap.end()) return (*it).second;
	// si l'image n'est pas défini on la crée
	GenericImage* img = new GenericImage(filename);
	cout << "loading image through GenericImage : ";
	cout << filename << "." << endl;
	m_imageMap[filename] = img;
	return img;
}

void MaterialGestionner::loadImage(string filename) {
	getImage(filename);
}



GLuint MaterialGestionner::forceLoadingTexture(string filename) {
	// does this texture already exists ?
	map<string,GLuint>::iterator it = m_textureMap.find(filename);
	if (it != m_textureMap.end()) {
		// yes : we reload it
		GLuint id = (*it).second;
		GenericImage* image = getImage(filename);
		// reloading image
		image->loadImageFromFile(filename);
		return image->defineTextureFromImage(id);
	} else {
		// no we create it
		return getTexture(filename);
	}
	

}
